package top.sqdpt.pt.statemachine.machine;

import lombok.Getter;
import top.sqdpt.pt.statemachine.action.PtStateAction;
import top.sqdpt.pt.statemachine.builder.PtStateTranslateBuilder;
import top.sqdpt.pt.statemachine.event.PtStateEvent;
import top.sqdpt.pt.statemachine.translate.PtStateTranslate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

/**
 * 抽象状态机
 * 需要继承该状态机，并实现initMachine()
 *
 * @param <S> 状态枚举
 * @param <E> 事件枚举
 */
public abstract class PtStateMachine<S extends Enum<S>, E extends Enum<E>> {

    private final Map<S, Map<E, TransitionInfo<S>>> stateMachineMap = new HashMap<>();

    /**
     * 在构造函数中初始化数据
     */
    public PtStateMachine() {
        PtStateTranslateBuilder<S, E> ptStateTranslateBuilder = new PtStateTranslateBuilder<>();
        List<PtStateTranslate<S, E>> ptStateTranslates = initMachine(ptStateTranslateBuilder);
        if (ptStateTranslates == null || ptStateTranslates.isEmpty()) {
            throw new IllegalArgumentException("stateTranslates 未初始化");
        }
        for (PtStateTranslate<S, E> ptStateTranslate : ptStateTranslates) {
            S sourceState = ptStateTranslate.getSourceState();
            S targetState = ptStateTranslate.getTargetState();
            PtStateEvent<S, E> stateEvent = ptStateTranslate.getStateEvent();
            if (stateEvent == null) {
                throw new IllegalArgumentException("未定义状态转换事件: " + sourceState + " -> " + targetState);
            }

            E event = stateEvent.getEvent();
            PtStateAction<S> action = stateEvent.getAction();
            if (action == null) {
                throw new IllegalArgumentException("未定义状态转换动作: " + sourceState + " -> " + event);
            }

            // 确保源状态的Map存在
            stateMachineMap.putIfAbsent(sourceState, new HashMap<>());

            // 检查重复定义
            if (stateMachineMap.get(sourceState).containsKey(event)) {
                throw new IllegalArgumentException("重复的状态转换定义: " + sourceState + " -> " + event);
            }

            // 保存状态转换信息
            stateMachineMap.get(sourceState).put(event, new TransitionInfo<>(targetState, action));
        }
    }

    public abstract List<PtStateTranslate<S, E>> initMachine(PtStateTranslateBuilder<S, E> ptStateTranslateBuilder);

    /**
     * 执行状态机设定好的事件
     *
     * @param sourceState 开始状态
     * @param event       事件
     * @param context     需要传递的上下文
     * @throws Exception
     */
    public void execute(S sourceState, E event, Object context) throws Exception {
        Map<E, TransitionInfo<S>> transitions = stateMachineMap.get(sourceState);
        if (transitions == null) {
            throw new IllegalArgumentException("未定义状态转换: " + sourceState);
        }

        TransitionInfo<S> transitionInfo = transitions.get(event);
        if (transitionInfo == null) {
            throw new IllegalArgumentException("未定义状态转换: " + sourceState + " -> " + event);
        }

        // 执行action
        PtStateAction<S> action = transitionInfo.getAction();
        if (action == null || !action.guard(sourceState, transitionInfo.getTargetState(), context)) {
            return;
        }
        try {
            action.handle(sourceState, transitionInfo.getTargetState(), context);
        } catch (Exception e) {
            action.onFail(sourceState, transitionInfo.getTargetState(), context, e);
        }
    }

    /**
     * 执行状态机设定好的事件
     * 异步执行
     *
     * @param sourceState 开始状态
     * @param event       事件
     * @param context     需要传递的上下文
     */
    public CompletableFuture<Void> executeAsync(S sourceState, E event, Object context, Executor executor) {
        return CompletableFuture.runAsync(() -> {
            try {
                execute(sourceState, event, context);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }, executor);
    }

    /**
     * 内部类，用于存储状态转换的目标状态和动作
     */
    @Getter
    private static class TransitionInfo<S extends Enum<S>> {
        private final S targetState;
        private final PtStateAction<S> action;

        public TransitionInfo(S targetState, PtStateAction<S> action) {
            this.targetState = targetState;
            this.action = action;
        }

    }
}
